package Stack::DES3550;

use strict;

use lib '..';

use Data::Dumper;
use Utils;
use NetAddr::IP;
use Logger;
use Conf; my $conf = Conf->show;

use base qw (Stack);

my $time_before;		# kvoli pocitaniu doby trvania jednotlivych operacii

sub initialize {
	# NEZMAZAT!!! inak bude deep recursion
}

sub get_serial_number {
	my $self = shift;
#	my ($unit) = @_;
#	my $OID = "mib-2.47.1.1.1.1.11.".(200+$unit);
#	return $self->SNMPget($OID);
	return "NOT_IMPLEMENTED";
}

sub SNMPgetMAClist {	
        my $self = shift;
        my ($vlan_id) = @_;
	return $self->SNMPwalk("mib-2.17.7.1.2.2.1.2.$vlan_id");
#        return $self->SNMPwalk("mib-2.17.4.3.1.2");
}

sub MAC2Port {		
        my $self = shift;
        my ($MACdec, $vlan_id) = @_;
#        return $self->SNMPget("mib-2.17.4.3.1.2.$MACdec") || 0;
	return $self->SNMPget("mib-2.17.7.1.2.2.1.2.$vlan_id.$MACdec") || 0;
}

sub switch_internal_port_count {
        my $self = shift;
        return 50;
}


sub main_uplink {       # zavadzame konvenciu, ze na D-Link DES-3550 bude VZDY uplinkovy port 1/50 !!
        my $self = shift;
        my $unit = 1;
        my $port = 50;
        return ($unit, $port);
}

sub CLI_prompt {
	my $self = shift;
#	return '>';	# prihlasenie do oper modu
	return '#';	# prihlasenie do enable modu
}

sub CLI_configure {
	my $self = shift;
	my ($commands) = @_;
	return $self->cmd("$commands\n");
}

sub CLI_quit {
	my $self = shift;
	return "logout\n";
}

sub CLI_saveconfig {
	my $self = shift;
#	my $config_name = $self->standard_conf_filename;
	return "save\n";
}

sub CLI_show_interface {
	my $self = shift;
#	my ($unit,$port) = @_;
#	my @status = $self->cmd("show interfaces status ethernet $unit/$port\n");
#	my @switchport = $self->cmd("show interfaces switchport ethernet $unit/$port\n");
#	my @counters = $self->cmd("show interfaces counters ethernet $unit/$port\n \n");
#	return join("",@status)."\n".join("",@switchport)."\n".join("",@counters);
#	return @status,@switchport,@counters;
	return "Due too interactive CLI management on D-Link DES-3550 feature not implemented. This could be fixed probably using SNMP, but I have no time to do it now, maybe later. If you are interested in settings, status, counters, CPU utilization, FDB, IGMP snooping state, VLAN state, SafeGuard or another parameters, use native web or telnet interface. ostry"
}

sub translatePortNumber {	#	portnum => unit, port
        my $self = shift;
        return (1, shift);
}

sub detranslatePortNumber {	#	unit, port => portnum
        my $self = shift;
        my ($unit,$port) = @_;
        return $port;
}


sub extended_IPACL_name {	# on D-Link DES-3550 deprecated
	my $self = shift;
	my ($unit, $port, $direction) = @_;
	return "unit".Utils::addZeros($unit,2)."port".Utils::addZeros($port,2).$direction;
}

sub unbind_extended_IPACL {
	my $self = shift;
#	Podla koncepcie ACL u D-Link DES-3550 su pravidla viazane priamo na porty "on the fly",
#	takze tato procedura na rozdiel od SMC nema opodstatnenie
#	my ($unit, $port, $direction) = @_;
#	if ($direction eq 'in' || $direction eq 'out') {
#		return $self->CLI_configure_interface($unit,$port,
#			"no ip access-group ".$self->extended_IPACL_name($unit,$port,$direction).
#											" $direction\n");
#	} else {
#		return '';
#	}
}


sub drop_extended_IPACL {
	my $self = shift;
	my ($unit, $port, $direction) = @_; # unit je vzdy 1 a direction vzdy in, ale nevadi
	my $aligned_port = Utils::addZeros($port,2);
	my $result = '';
# konvencia access_id: AAPPR = AA (max. 2-ciferne access_profile) + PP (VZDY 2-ciferne cislo portu) + R (1-ciferne cislo pravidla, zaciname od nuly)
# D-Link DES-3550 podporuje: 1-65535 access_id, 1-255 profile_id, 1-50 cislo portu
# na ucely LANmgmt obmedzime profile_id na 10,20,30,40,50 (pozri procesny dokument LANmgmt_install_stack), access_id preto max. 50509 - v kazdom profile_id kazdeho portu teda umoznime maximalne 10 pravidiel, co je nateraz dostatocne... cele sa nam to nejako zamotava a zozlozituje, takze do buducna tento sposob asi zavrhneme - ked IEEE 802.1x dozreje, a my s nim ;)

my @allowed_access_profiles = (10,20,30,40,50);
        if ($direction eq 'in' || $direction eq 'out') {
		foreach my $profile_id (@allowed_access_profiles) {
			for (my $rule_number = 0; $rule_number <= 5; $rule_number++) {
	                	$result .= $self->CLI_configure(
					"config access_profile profile_id $profile_id delete access_id ".
					"$profile_id $aligned_port$rule_number\n"
				);
				if ($profile_id == 10 || $profile_id > 20) {
					# 5 pravidiel mazeme len pri access_profile 20
					# spinave riesenie, ale moze za to koncepcia ACL u D-Link DES-3550
					last;
				}
			}
		}
		return $result;
        } else {
                return '';
        }
}

sub enter_extended_IPACL_rules {
        my $self = shift;
#	Na rozdiel od SMC je u D-Link DES-3550 tato funkcia zbytocna, ale kvoli kompatibilite so zvyskom LANmgmt zostava.
#        my ($unit, $port, $direction, $commands) = @_; # unit je vzdy 1 a direction vzdy in, ale nevadi
#	my $aligned_port = Utils::addZeros($port,2);
#	my $result = '';
#        if ($direction eq 'in' || $direction eq 'out') {
#                return $self->CLI_configure("access-list ip extended ".
#				$self->extended_IPACL_name($unit,$port,$direction)."\n$commands\nexit\n");
#        } else {
#                return '';
#        }
}

sub IPACL_deny_dhcp_server {
        my $self = shift;
        my ($unit, $port, $direction) = @_; # unit je vzdy 1 a direction vzdy in, ale nevadi
	my $aligned_port = Utils::addZeros($port,2);
	my $result = '';
	return $self->CLI_configure("config access_profile profile_id 10 add access_id 10${aligned_port}0 ".
					"ip udp dst_port 68 port $port deny\n");
#        return $self->enter_extended_IPACL_rules($unit,$port,$direction,
#								"deny UDP any any destination-port 68\n");
}

# deprecated, ostry 8.1.2006
#sub IPACL_permit_dhcp_client {
#        my $self = shift;
#        my ($unit, $port, $direction) = @_; # unit je vzdy 1 a direction vzdy in, ale nevadi
#	my $aligned_port = Utils::addZeros($port,2);
#	my $result = '';
#	return $self->CLI_configure("config access_profile profile_id 30 add 30${aligned_port}9 ".
#					"ip destination_ip 255.255.255.255 port $port permit\n");
#        return $self->enter_extended_IPACL_rules($unit,$port,$direction,
#								"permit host 0.0.0.0 host 255.255.255.255\n");
#}

sub IPACL_permit_destinations {
        my $self = shift;
        my ($unit, $port, $direction, $destinations) = @_; # unit je vzdy 1 a direction vzdy in, ale nevadi
	my $aligned_port = Utils::addZeros($port,2);
	my $result = '';
	my $rules;
	my $mask32count = 0; my $mask27count = 0;	# ked bude cas, tak zovseobecnim
	foreach my $destination (@{$destinations}) {
		my $ip = NetAddr::IP->new($destination);
		my $mask = $ip->mask; my $masklen = $ip->masklen; $ip = $ip->addr;
		if ($masklen == 32) {
			$mask32count++;
			$rules .= "config access_profile profile_id 30 add access_id 30${aligned_port}$mask32count ".
					"ip destination_ip $ip port $port permit\n";
		} elsif ($masklen == 27) {
			$mask27count++;
			$rules .= "config access_profile profile_id 40 add access_id 40${aligned_port}$mask27count ".
					"ip destination_ip $ip port $port permit\n";
		} else {
			Logger->write(__PACKAGE__." Destination mask $mask not supported in LANmgmt using ACL of D-Link DES-3550!");
		}
#                $rules .= "permit any $ip $mask\n";
	}
	return $self->CLI_configure($rules);
#        return $self->enter_extended_IPACL_rules($unit,$port,$direction,$rules);
}

sub IPACL_permit_sources {
        my $self = shift;
        my ($unit, $port, $direction, $sources) = @_;
	my $aligned_port = Utils::addZeros($port,2);
	my $result = '';
        my $rules; my $sources_count = 0;
        foreach my $source (@{$sources}) {
		my $ip = NetAddr::IP->new($source); 
		my $mask = $ip->mask; $ip = $ip->addr; 
		$rules .= "config access_profile profile_id 20 add access_id 20${aligned_port}$sources_count ".
				"ip source_ip $ip port $port permit\n";
		$sources_count++;
#                $rules .= "permit $ip $mask any\n";
        }
	return $self->CLI_configure($rules);
#        return $self->enter_extended_IPACL_rules($unit,$port,$direction,$rules);
}

sub IPACL_deny_default {
        my $self = shift;
        my ($unit, $port, $direction) = @_;
	my $aligned_port = Utils::addZeros($port,2);
	my $result = '';
        my $rules;
	return $self->CLI_configure(
		"config access_profile profile_id 30 add access_id 30${aligned_port}9 ".
		"ip destination_ip 255.255.255.255 port $port permit\n".
		"config access_profile profile_id 50 add access_id 50${aligned_port}9 ".
		"ip source_ip 0.0.0.0 destination_ip 0.0.0.0 port $port deny\n");
#        return $self->enter_extended_IPACL_rules($unit,$port,$direction,
#				"permit host 0.0.0.0 host 255.255.255.255\ndeny any any\n");
#ked niekto ziska IP, a chce si ju obnovit, pripadne mu to
#neprideli adresu z 0.0.0.0 (teda ma automaticku privatnu alebo uz pridelenu
#a nejaku chybnu), tak pri prvidle allow 0.0.0.0 255.255.255.255 sa mu uz
#adresu nepodari obnovit, iba ak ju releasne a obnovi z 0
#				"permit any host 255.255.255.255\ndeny any any\n");
}

sub bind_extended_IPACL {
        my $self = shift;
#	Podla koncepcie ACL u D-Link DES-3550 su pravidla viazane priamo na porty "on the fly",
#	takze tato procedura na rozdiel od SMC nema opodstatnenie
#        my ($unit, $port, $direction) = @_;
#        if ($direction eq 'in' || $direction eq 'out') {
#                return $self->CLI_configure_interface($unit,$port,"\nip access-group ".
#				$self->extended_IPACL_name($unit,$port,$direction)." $direction\n");
#        } else {
#                return '';
#        }
}

# deprecated - pouzivat max_bitrate
#sub set_switchport_broadcast_max_packet_rate {
#	my $self = shift;
#	my ($unit, $port, $bsc) = @_;
#	if (!defined $bsc || !$bsc) {
#		return $self->CLI_configure_interface($unit,$port,"no switchport broadcast packet-rate\n");
#	} else {
#		return $self->CLI_configure_interface($unit,$port,"switchport broadcast packet-rate $bsc\n");
#	}
#}

# deprecated - nastavuje sa rucne pre cely switch, ostry 8.1.2007
#sub set_switchport_broadcast_max_bitrate {
#	my $self = shift;
#	my ($unit, $port, $bitrate) = @_;
#	if (!defined $bitrate || !$bitrate) {
#		return $self->CLI_configure_interface($unit,$port,
#						"no switchport broadcast packet-rate\n");
#	} else {
#		my $byterate = Utils::round($bitrate/8);
#		my $packetrate = Utils::round($byterate/(Utils::ethernet_max_frame_size()));
#		if ($packetrate < 500) {
#			$packetrate = 500;
#		}
#		return $self->CLI_configure_interface($unit,$port,
#						"switchport broadcast packet-rate $packetrate\n");
#	}
#}

sub set_switchport_description {	# D-Link DES-3550 = max. 32 znakov !!! osetrit dakedy !!
	my $self = shift;
	my ($unit, $port, $description) = @_;
	return $self->CLI_configure("config ports $port description \"$description\"\n");
}

sub set_switchport_access_VLAN {
	my $self = shift;
	my ($unit, $port, $vlan_id) = @_;
#	my $switch = $self->getUnit($unit);
#	my $plug = $switch->Port2Plug($port);
	my $segment = Segment->retrieve($vlan_id);
	my $vlan_name = $segment->vlan_name;
	# get actual vlan_id for this port
	my $former_vlan_id = $self->SNMPget("mib-2.17.7.1.4.5.1.1.$port");
	my $former_segment = Segment->retrieve($former_vlan_id);
	my $former_vlan_name = $former_segment->vlan_name; 
#	my $plug = Plug->search( port => $port, un );
	$self->CLI_configure("config vlan $former_vlan_name delete $port\n"); 
	$self->CLI_configure("config vlan $vlan_name add untagged $port\n"); 
	$self->CLI_configure("config gvrp $port ingress_checking enable\n"); 
#	my $set_result = $self->CLI_configure_interface($unit,$port,
#		"switchport allowed vlan add $vlan_id untagged\n".
#		"switchport native vlan $vlan_id\n".
#		"switchport ingress-filtering\n".
#		"switchport mode hybrid\n".
#		"no switchport gvrp\n"
#	);

#	Na rozdiel od SMC6824M, u D-Link DES-3550 tato procedura neodstrani z portu netagovane VLAN.
#	Je to potencialne riziko, preto ked niekto pride na to, ako to efektivne urobit, moze dat vediet,
#	alebo to rovno implementovat.
#	my @show_switchport_result = $self->CLI_show("interfaces switchport ethernet $unit/$port");
#	my @VLANs;
#	foreach (@show_switchport_result) {	# vyparsovanie nastavenych VLAN z vystupu
#		my @var_val = split ':',$_;
#		if ($var_val[0] =~ /^.*Allowed VLAN$/) {
#			my @raw_vlan_ids = split /\(/,$var_val[1];
#			foreach (@raw_vlan_ids) {
#				$_ =~ /^\D*(\d+)\D*$/;
#				if ($_ != $vlan_id) {		# tu spravnu VLAN ignoruj
#					push @VLANs, $1;			
#				}
#			}
#		}
#	}
#
#	# zrus ostatne VLAN, ktore tam nemaju byt nastavene
#	my $unset_result;
#	foreach (@VLANs) {
#		$unset_result = $self->CLI_configure_interface($unit,$port,
#			"switchport allowed vlan remove $_\n"
#		);
#	}
}

sub CLI_show {
	my $self = shift;
	my ($what) = @_;
	return $self->cmd("show $what\n")
}

sub backupStartupConfig {
	my $self = shift;
	my $dstfile = $self->standard_conf_filename;
	my $tftp_ip=$conf->{TFTP_IP};
	$self->cmd("upload configuration $tftp_ip $dstfile\n");
}

sub backupRunningConfig {
	my $self = shift;
	my $dstfile = $self->standard_conf_filename.'_running';
	my $tftp_ip=$conf->{TFTP_IP};
	Logger->write(__PACKAGE__." D-Link DES-3550 does not support uploading running configuration to TFTP. Therefore startup configuration upload to TFTP performed.");
	$self->backupStartupConfig;
#	$self->cmd("\ncopy running-config tftp\n$tftp_ip\n$dstfile\n");
}

sub set_static_addr_uplink {	# nastavi na zvolenej VLAN hlavneho uplinku staticku MAC (typicky default gw)
				# (bezpecnostna feature)
	my $self = shift;
	my ($MAC, $VLAN) = @_;
	my ($unit, $port) = $self->main_uplink;
	$MAC = Utils::MACcolon2minus($MAC);
        my $segment = Segment->retrieve($VLAN);
        my $vlan_name = $segment->vlan_name;
	my $CLIcommand = "create fdb $vlan_name $MAC port $port\n";
	$self->cmd($self->CLI_configure($CLIcommand));
}

sub set_port_max_MAC_count {
	my $self = shift;
	my ($unit, $port, $count) = @_;
	$self->cmd("config port_security ports $port admin_state enable max_learning_addr $count lock_address_mode DeleteOnTimeout\n");
}

sub CLI_set_RADIUS {	# nastavi RADIUS secret key a  RADIUS servery
			# je mozne, ze bude najskor zmazat existujuce, ale to treba riesit rucne
			# alebo poloautomaticky
	my $self = shift;
	my ($key, @hosts) = @_;
	my $CLI_command;
	foreach my $host (@hosts) {
		$CLI_command .= "create authen server_host $host protocol radius key \"$key\"\n";
	}
	return $CLI_command;
}

sub CLI_set_GVRP {	# nastavi/odstavi globalne GVRP
	my $self = shift;
	my ($turned_on) = @_;
	if ($turned_on) {
		return "enable gvrp\n";
	} else {
		return "disable gvrp\n";

	}
}

sub CLI_set_SNTP {	# nastavi synchronizaciu hodin s casovymi servermi
	my $self = shift;
	my ($host1, $host2) = @_;

	if (!defined $host2 || !$host2) {
		$host2 = $host1
	}

	if ($host1) {
		return "enable sntp\nconfig sntp primary $host1 secondary $host2\n";
	} else {
		return "disable sntp\n";
	}
}

sub CLI_set_ident {	# nastavi na zariadeni hostname, CLI prompt, location a kontakt na admina
	my $self = shift;
	my ($hostname, $location, $admin_email) = @_;
	return "config snmp system_name $hostname\nconfig command_prompt \"$hostname\"\nconfig snmp system_location \"$location\"\nconfig snmp system_contact \"$admin_email\"\n";
}

sub CLI_set_SNMPcommunity {	# nastavi rw/ro community string alebo ho odstavi
	my $self = shift;
	my ($string, $type) = @_;
	if ($type) {
		if ($type == 7) {	# 7 = read-write community string
			return "create snmp community $string view CommunityView read_write\n";
		} else {		# readonly community string
			return "create snmp community $string view CommunityView read_only\n";
		}
	} else {			# delete community string
		return "delete snmp community $string\n";
	}
}

sub CLI_set_syslog {
	my $self = shift;
	my ($host, $off) = @_;
	if ($off) {
		return "delete syslog host 1\ndelete syslog host 2\ndelete syslog host 3\ndelete syslog host 4\n";
	} else {
		return "delete syslog host 1\create syslog host 1 ipaddress $host\n";
	}
}

sub CLI_set_logging {
	my $self = shift;
	my ($facility, $history_RAM, $history_flash) = @_;
	my @facility_table;
	$facility_table[16] = 'local0';
	$facility_table[17] = 'local1';
	$facility_table[18] = 'local2';
	$facility_table[19] = 'local3';
	$facility_table[20] = 'local4';
	$facility_table[21] = 'local5';
	$facility_table[22] = 'local6';
	$facility_table[23] = 'local7';
		
	return "config syslog host all severity all facility $facility_table[$facility]\n"; 
}

sub CLI_set_STP {
	my $self = shift;
	my ($mode) = @_;
	return "config stp version $mode\n";
}

sub CLI_configure_interface {
	my $self = shift;
	my ($unit, $port, $commands) = @_;
#	return $self->CLI_configure("interface ethernet $unit/$port\n$commands\nexit\n");
	return "";	# metoda prevzata z SMC6824M nema u D-Link DES-3550 ekvivalent
}

1;
